/**
 * =====================================================================================
 *       @file  ngx_epoll_modules.c
 *      @brief  
 *
 *  Detailed description starts here.
 *
 *   @internal
 *     Created  2012年05月11日 14时14分27秒 
 *    Revision  1.0.0.0
 *    Compiler  gcc/g++
 *
 *     @author  luis
 * =====================================================================================
 */
#include <stdint.h>
#include <errno.h>
#include <sys/epoll.h>
#include "ngx_core.h"
#include "ngx_event.h"
#include "ngx_conf_file.h"
#include "ngx_string.h"
#include "ngx_cycle.h"
#include "ngx_connection.h"
#include "ngx_palloc.h"
#include "ngx_event_posted.h"

static ngx_int_t ngx_epoll_init(ngx_cycle_t *cycle);
static ngx_int_t ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
static ngx_int_t ngx_epoll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
static ngx_int_t ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_uint_t timer, ngx_uint_t flags);

static int                       ep = -1;
static struct epoll_event       *event_list;
static ngx_uint_t                nevents;

static ngx_str_t epoll_name = ngx_string("epoll");

static ngx_command_t ngx_epoll_commands[] =
{
    ngx_null_command
};

ngx_event_module_t ngx_epoll_module_ctx = {
    &epoll_name,
    NULL,
    NULL,

    {
        ngx_epoll_add_event,            /* add an event */
        ngx_epoll_del_event,            /* delete an event */
        ngx_epoll_add_event,            /* enable an event */
        ngx_epoll_del_event,            /* disable an event */
        NULL,
        NULL,
        NULL,
        ngx_epoll_process_events,       /* process the events */
        ngx_epoll_init,
        NULL,
    }
};

ngx_module_t ngx_epoll_module =
{
    NGX_MODULE_V1,
    &ngx_epoll_module_ctx,
    ngx_epoll_commands,
    NGX_EVENT_MODULE,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NGX_MODULE_V1_PADDING
};


static ngx_int_t ngx_epoll_init(ngx_cycle_t *cycle)
{
    if (ep == -1)       //还没有创建epoll
    {
        ep = epoll_create(cycle->connection_n / 2); 
        if (ep == -1)
        {
            perror("epoll create"); 
            return NGX_ERROR;
        }
    }

    nevents = 512;
    event_list = ngx_alloc(sizeof(struct epoll_event) * nevents);
    if (event_list == NULL)
    {
        return NGX_ERROR;
    }
    
    ngx_event_actions = ngx_epoll_module_ctx.actions;

    return NGX_OK;
}


static ngx_int_t ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
{
    int                 op;    
    uint32_t            events, prev;
    ngx_event_t        *e;
    ngx_connection_t   *c;
    struct epoll_event  ee;

    c = ev->data;

    events = (uint32_t)event;

    if (event == NGX_READ_EVENT)
    {
        e = c->write; 
        prev = EPOLLOUT;
    }
    else
    {
        e = c->read; 
        prev = EPOLLIN;
    }

    if (e->active)
    {
        op = EPOLL_CTL_MOD; 
        events |= prev;
    }
    else
    {
        op = EPOLL_CTL_ADD; 
    }

    ee.events = events | (uint32_t)flags; 
    ee.data.ptr = (void *)((uintptr_t)c | ev->instance);

    printf("ngx add event:fd:%d op:%d ev:%08XD", c->fd, op, ee.events);

    if (epoll_ctl(ep, op, c->fd, &ee) == -1)
    {
        printf("epoll_ctl failed:%d", errno); 
        return NGX_ERROR;
    }

    ev->active = 1;

    return NGX_OK;
}

static ngx_int_t ngx_epoll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
{
    int                 op;    
    uint32_t            prev;
    ngx_event_t        *e;
    ngx_connection_t   *c;
    struct epoll_event  ee;

    if (flags & NGX_CLOSE_EVENT)
    {
        ev->active = 0;
        return NGX_OK;
    }

    c = ev->data;

    if (event == NGX_READ_EVENT)
    {
        e = c->write; 
        prev = EPOLLOUT;
    }
    else
    {
        e = c->read; 
        prev = EPOLLIN;
    }

    if (e->active)
    {
        op = EPOLL_CTL_MOD; 
        ee.events = prev | (uint32_t)flags; 
        ee.data.ptr = (void *)((uintptr_t)c | ev->instance);
    }
    else
    {
        op = EPOLL_CTL_DEL; 
        ee.events = 0;
        ee.data.ptr = NULL;
    }


    printf("ngx del event:fd:%d op:%d ev:%08XD", c->fd, op, ee.events);

    if (epoll_ctl(ep, op, c->fd, &ee) == -1)
    {
        perror("epoll_ctl failed"); 
        return NGX_ERROR;
    }

    ev->active = 0;

    return NGX_OK;
}

static ngx_int_t ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_uint_t timer, ngx_uint_t flags)
{
    int                 events;
    uint32_t            revents;
    ngx_int_t           instance, i;
    ngx_uint_t          level;
    ngx_event_t        *rev, *wev, **queue;
    ngx_connection_t   *c;
    int                 err;
    

    printf("epoll wait\n");
    events = epoll_wait(ep, event_list, (int)nevents, timer);

    err = (events == -1) ? errno : 0;
    if (err)
    {
        if (err == EINTR)
        {
            printf("epoll eintr");
            return NGX_OK; 
        }

        perror("epoll wait");
        return NGX_ERROR;
    }

    if (events == 0)
    {
        return NGX_OK; 
    }

    //进程里暂不支持多线程，不需要Mutex同步
    for (i = 0; i < events; ++i)
    {
        c = event_list[i].data.ptr; 

        instance = (uintptr_t)c & 1;
        c = (ngx_connection_t *)((uintptr_t)c & (uintptr_t)~1);

        rev = c->read;
        if (c->fd == -1 || rev->instance != instance)
        {
            perror("epoll");
            continue; 
        }

        revents = event_list[i].events;
        //XXX 不明白
        if ((revents & (EPOLLERR | EPOLLHUP)) && (revents & (EPOLLIN | EPOLLOUT)) == 0)
        {
            revents |= EPOLLIN | EPOLLOUT; 
        }

        if ((revents & EPOLLIN) && rev->active)
        {
            rev->ready = 1;
            if (flags & NGX_POST_EVENTS)
            {
                //前面获得了锁，就要post执行，以免柱塞别到进程获得锁 
                queue = (ngx_event_t **)(rev->accept ? &ngx_posted_accept_events : &ngx_posted_events);     
                ngx_locked_post_event(rev, queue);
            }
            else
            {
                rev->handler(rev);
            }
        }

        //XXX 判断写事件
    }
     

    return NGX_OK;
}
